package cacheserial;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

/**
 * 缓存串行化队列
 * 通过配置传入初始化的队列大小
 *
 * @author Aimy
 * @createTime 2021/6/17 10:25
 **/
public class CacheSerialQueue {

    /**
     * 队列数组，每个线程监听一个队列
     */
    private static volatile List<BlockingQueue<ICacheSerialRequest>> cacheQueueList;

    /**
     * 标记是否有重复的读请求
     */
    private static Map<Object, Boolean> flagMap = new HashMap<>();

    private CacheSerialQueue() {
    }

    /**
     * 初始化监听队列
     *
     * @return
     */
    public static List<BlockingQueue<ICacheSerialRequest>> getQueueInstance() {
        if (cacheQueueList == null) {
            synchronized (CacheSerialQueue.class) {
                if (cacheQueueList == null) {
                    cacheQueueList = new ArrayList<BlockingQueue<ICacheSerialRequest>>();
                }
            }
        }
        return cacheQueueList;
    }

    /**
     * 添加到队列
     *
     * @param queue
     */
    public static void addQueue(BlockingQueue<ICacheSerialRequest> queue) {
        getQueueInstance().add(queue);
    }

    /**
     * 根据ID计算一个Queue
     *
     * @param key
     * @return
     */
    public static BlockingQueue<ICacheSerialRequest> getQueue(Object key) {
        int h;
        int hashKey = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        int index = (getQueueInstance().size() - 1) & hashKey;
        return getQueueInstance().get(index);
    }

    /**
     * 将请求添加到队列中
     * 写请求都要加入进去，并置为true
     * 读请求只加入一个，并置为false
     *
     * @param request
     * @return
     */
    public static boolean addRequest(ICacheSerialRequest request) {

        switch (request.getType()) {
            //读请求
            case READ:
                Boolean flag = flagMap.get(request.getKey());
                boolean hasRead = flag != null && !flag;
                if (hasRead) {
                    //之前队列中有读请求，则直接返回
                    return false;
                }
                //如果队列中没有读则置为false
                flagMap.put(request.getKey(), false);
                break;
            //写请求
            case WRITE:
                flagMap.put(request.getKey(), true);
                break;
        }

        //加入到队列
        return getQueue(request.getKey()).add(request);

    }

}
